﻿using System;
using System.Collections;
using System.Linq;
using CardPlay.Model.CardModel;
using NUnit.Framework;

namespace CardPlay.Tests.Model.CardModel
{
    [TestFixture]
    public class CardFaceTests
    {
        [Test]
        public void TestTheStructureOfAStandardDeck()
        {
            var deck = CardFace.StandardSet.ToList();

            Assert.AreEqual(CardFace.CardsPerStandardDeck, deck.Count);

            Assert.IsTrue(deck.GroupBy(face => face.Suit).All(g => g.Distinct().Count() == CardFace.RanksPerSuit));
            Assert.IsTrue(deck.GroupBy(face => face.Rank).All(g => g.Distinct().Count() == CardFace.SuitsPerDeck));

            Assert.IsTrue(CardFace.AllSuits.SequenceEqual(deck.GroupBy(face => face.Suit).Select(g => g.Key)));
        }

        [TestCaseSource("TestCasesForEquality")]
        public bool TestEquality(CardFace face1, CardFace face2)
        {
            return Equals(face1, face2);
        }

        [TestCaseSource("TestCasesForParse")]
        public CardFace TestParse(string expression)
        {
            return CardFace.Parse(expression);
        }

        public static IEnumerable TestCasesForParse
        {
            get
            {
                yield return
                    new TestCaseData("three of hearts")
                        .SetDescription("Case insensitive")
                        .Returns(new CardFace(Rank.Three, Suit.Hearts));

                yield return
                    new TestCaseData("Ace of Diamonds")
                        .SetDescription("Case insensitive")
                        .Returns(new CardFace(Rank.Ace, Suit.Diamonds));

                yield return
                    new TestCaseData("5 of spades")
                        .SetDescription("Numeric rank")
                        .Returns(new CardFace(Rank.Five, Suit.Spades));

                yield return
                    new TestCaseData(String.Empty)
                        .SetDescription("Empty string")
                        .Throws(typeof(FormatException));

                yield return
                    new TestCaseData("What were you thinking!")
                        .SetDescription("Invalid expression")
                        .Throws(typeof(FormatException));

                yield return
                    new TestCaseData("three of heart")
                        .SetDescription("Invalid suit")
                        .Throws(typeof(FormatException));

                yield return
                    new TestCaseData("27 of spades")
                        .SetDescription("Invalid rank")
                        .Throws(typeof(FormatException));
            }
        }

        public static IEnumerable TestCasesForEquality
        {
            get
            {
                var cardFace = new CardFace(Rank.Three, Suit.Hearts);

                yield return
                    new TestCaseData(cardFace, cardFace)
                        .SetDescription("Same instance")
                        .Returns(true);

                yield return
                    new TestCaseData(cardFace, new CardFace(Rank.Three, Suit.Hearts))
                        .SetDescription("Same value")
                        .Returns(true);

                yield return
                    new TestCaseData(cardFace, new CardFace(Rank.Three, Suit.Spades))
                        .SetDescription("Different suit")
                        .Returns(false);

                yield return
                    new TestCaseData(cardFace, new CardFace(Rank.Two, Suit.Hearts))
                        .SetDescription("Different rank")
                        .Returns(false);
            }
        }
    }
}
